#include "udpnode.h"
#include <iostream>
#include <string.h>
#include <arpa/inet.h>

UdpNode::UdpNode(int Port)
    : port(Port)
    , socket_udp(-1)
    , pack(NULL)
    , upstream(NULL)
{
}

UdpNode::~UdpNode()
{
}

// Return 1 means start successed. Return 0 means start failed.
int UdpNode::start() {

    // Start UDP port
    if ((socket_udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        std::cout << __FILE__ << ", Socket setup failed:" << __LINE__ << std::endl;
        return 0;
    }

    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(port);

    memset(&client_addr, 0, sizeof(client_addr));
    sizeofClient = sizeof(client_addr);

    if ( bind(socket_udp, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0 ) {
        std::cout << __FILE__ << ", BindFailed :" << __LINE__ << std::endl;
        return 0;
    }

    // Create Receive thread
    int re = pthread_create( &thread_recv, NULL, &UdpNode::receiveUdpPack, (void*)this);

//    std::cout << __FILE__ << ":" << __LINE__ << ", create receive thread : " << re << std::endl;
    if (re != 0) return 0;

    return 1;
}


void UdpNode::sendUdpPack(const std::string& message)
{
    if( sendto(socket_udp, message.c_str(), message.size(), 0, (sockaddr*)&client_addr, sizeofClient) < 0 ) {
        std::cout << "UdpNode, send message failed :" << message << std::endl;
    }
    else {
        std::cout << "UdpNode, send message success :" << message << std::endl;
    }
}

void UdpNode::setRemote(const std::string& IP, int Port) {
    memset(&client_addr, 0, sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = inet_addr(IP.c_str());
    client_addr.sin_port = htons(Port);
}

void UdpNode::setPacker(TransPack<TWindow> *Packer) {
    pack = Packer;
}

void* UdpNode::receiveUdpPack(void* udpNode) {
    UdpNode* node = (UdpNode*)udpNode;
    assert(node->upstream != NULL);
    while (true) {

          int pack_size = recvfrom(node->socket_udp, node->buf, BUFSIZE, 0, (sockaddr*)&(node->client_addr), (socklen_t*)&(node->sizeofClient));
          // End the string
          node->buf[pack_size] = 0;
          std::cout << "ReceiveUdpPack: content = " << node->buf <<  std::endl;
          // Try to get lock, if failed just keep quering.
          while (! node->upstream->getLock() ) {
              std::cout << "Cannot get Lock" << std::endl;
          };

          node->upstream->Lock();

          if (node->pack != NULL) {
              node->pack->addLine(node->buf);
          }
          memset(node->buf, 0, strlen(node->buf));

          node->upstream->unLock();
    }

    return (void*)0;
}
